#include "persistence.h"
#include <vector>
#include <algorithm>
#include "ptime.h"
#include "config.h"
#include "config.h"
#include "plog.h"
#include <esp_heap_caps.h>

Preferences pref_handler;

void Persistence_write_entry(const char *key, PersistenceData_t *data)
{
  Persistence_write_entries(key, data, 1);
}

void Persistence_write_entries(const char *key, PersistenceData_t *data, size_t length)
{
  pref_handler.begin(PERSISTENCE_NAMESPACE, false);
  pref_handler.putBytes(key, (uint8_t *)data, sizeof(PersistenceData_t) * length);
  pref_handler.end();
}

size_t Persistence_read_entry(const char *key, PersistenceData_t *data)
{
  return Persistence_read_entries(key, data, 1);
}

size_t Persistence_read_entries(const char *key, PersistenceData_t *data, size_t length)
{
  pref_handler.begin(PERSISTENCE_NAMESPACE, true);
  size_t size = pref_handler.getBytes(key, (uint8_t *)data, sizeof(PersistenceData_t) * length);
  pref_handler.end();
  return size;
}

void Persistence_delete_entry(const char *key)
{
  pref_handler.begin(PERSISTENCE_NAMESPACE, false);
  pref_handler.remove(key);
  pref_handler.end();
}

void Persistence_test(void)
{
  PersistenceData_t sensor_data = {
      .tvoc = 1024,
      .eco2 = 8,
      .temperature = 34.5,
      .humidity = 12.3,
  };
  Persistence_write_entry("1024", &sensor_data);
  PersistenceData_t sensor_data_2;
  size_t size = Persistence_read_entry("1024", &sensor_data_2);
  Serial.printf("size: %d-  %d\n", size, sizeof(PersistenceData_t));
  for (int i = 0; i < size; i++)
  {
    Serial.printf("0x%02x(%d),", ((uint8_t *)&sensor_data_2)[i], ((uint8_t *)&sensor_data_2)[i]);
  }
  Serial.println();
  Serial.println(sensor_data_2.tvoc);
  Serial.println(sensor_data_2.eco2);
  Serial.println(sensor_data_2.temperature);
  Serial.println(sensor_data_2.humidity);
}

PersistenceData_t Persistence_convert(GS_result_t *gs, ES_result_t *es)
{
  PersistenceData_t data = {
      .tvoc = gs->tvoc,
      .eco2 = gs->eco2,
      .temperature = es->temperature,
      .humidity = es->humidity,
  };
  return data;
}
char *Persistence_format(PersistenceData_t *data)
{
  static char buf[100];
  memset(buf, 0, sizeof(buf));
  sprintf(buf, "eCO2: %d ppm\tTVOC: %d ppb\tTemperature: %.2f *C\tHumidity: %.2f %%",
          data->eco2, data->tvoc, data->temperature, data->humidity);
  return buf;
}
////////////////////////////////////////////////// app层代码

// 改为uint32_t支持更大范围，指向下一个待写的位置，也就是最新数据的下一个位置
static uint32_t key_index = 0;
static uint32_t key_buffer[PERSISTENCE_KEY_MAX_NUM] = {}; // 自动初始化为全空

void Persistence_init()
{
  pref_handler.begin(PERSISTENCE_NAMESPACE, false);
  Persistence_restore_meta();
  pref_handler.end();
}

void Persistence_restore_meta()
{
  key_index = pref_handler.getUInt(PERSISTENCE_KEY_DATA_META_KEY_INDEX);
  pref_handler.getBytes(PERSISTENCE_KEY_DATA_META_KEY_BUF, (uint8_t *)key_buffer, PERSISTENCE_KEY_MAX_NUM * sizeof(uint32_t));
  LOG_INFO("restore meta. key_index: %d", key_index);
  for (int i = 0; i < PERSISTENCE_KEY_MAX_NUM; i++)
    LOG_INFO("key_buffer: [%d] %d", i, key_buffer[i]);
  LOG_INFO("restore meta done");
}

void Persistence_write_meta()
{
  size_t res1 = pref_handler.putUInt(PERSISTENCE_KEY_DATA_META_KEY_INDEX, key_index);
  size_t res2 = pref_handler.putBytes(PERSISTENCE_KEY_DATA_META_KEY_BUF, (uint8_t *)key_buffer, PERSISTENCE_KEY_MAX_NUM * sizeof(uint32_t));
  LOG_INFO("write meta. key_index: %d, res1: %d, res2: %d", key_index, res1, res2);
  LOG_INFO("write meta entries: %d", pref_handler.freeEntries());

  LOG_DEBUG("write meta done");
}

void Persistence_write_data(uint32_t ts, GS_result_t *gs, ES_result_t *es)
{
  PersistenceData_t data = Persistence_convert(gs, es);
  pref_handler.begin(PERSISTENCE_NAMESPACE, false);

  // 这里考虑把ts当做key，也就是1分钟一个key
  // todo 后面可以考虑一天一个key，里面数据拼起来
  // 存储8位字符+终止符
  // uint32_t 4字节，8个16进制位
  // 滚动存储逻辑：达到上限时删除最旧条目
  if (key_index >= PERSISTENCE_KEY_MAX_NUM)
  {
    uint32_t oldest_ts = key_index % PERSISTENCE_KEY_MAX_NUM;
    pref_handler.remove(timestamp_key(key_buffer[oldest_ts]));
    key_index = oldest_ts;
  }
  // 提前更新写进去之后的状态
  // 即使meta更新了，而因为断电而data没有更新，这样恢复时会读不到该数据也没事。宁愿这样，而如果先写data，再写meta，万一中间断电，恢复时将会有旧数据残留。
  key_buffer[key_index] = ts;
  key_index++;
  Persistence_write_meta();

  LOG_DEBUG("write data\r\n   key: %d, %s, idx%d\r\n   data: %s", ts, timestamp_key(ts), key_index - 1, Persistence_format(&data));

  pref_handler.putBytes(timestamp_key(ts), (uint8_t *)&data, sizeof(PersistenceData_t));
  pref_handler.end();
}
// todo，如要ts，可加入参uint32_t *ts
uint32_t Persistence_read_all_data(PersistenceData_t *data)
{
  bool full = key_buffer[(key_index + 1) % PERSISTENCE_KEY_MAX_NUM] != 0;

  uint32_t key_index_tmp = key_index;
  uint32_t s = key_index;
  if (full)
    s = PERSISTENCE_KEY_MAX_NUM;

  PersistenceData_t *data_tmp = data;
  pref_handler.begin(PERSISTENCE_NAMESPACE, true);

  if (!full)
  {
    for (uint32_t i = 0; i < key_index_tmp; i++)
    {
      if (key_buffer[i] == 0)
        continue;
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&(data_tmp[i]), sizeof(PersistenceData_t));
    }
  }
  else
  {
    // 拼接
    uint32_t idx = 0;
    for (uint32_t i = key_index_tmp; i < PERSISTENCE_KEY_MAX_NUM; i++)
    {
      if (key_buffer[i] == 0)
        continue;
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&(data_tmp[idx++]), sizeof(PersistenceData_t));
    }
    for (uint32_t i = 0; i < key_index_tmp; i++)
    {
      if (key_buffer[i] == 0)
        continue;
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&(data_tmp[idx++]), sizeof(PersistenceData_t));
    }
  }

  pref_handler.end();

  return s;
}

void Persistence_traverse_all_data(Persistence_traverse_cb cb)
{
  bool full = key_buffer[(key_index + 1) % PERSISTENCE_KEY_MAX_NUM] != 0;

  uint32_t key_index_tmp = key_index;

  PersistenceData_t data_tmp = {};
  pref_handler.begin(PERSISTENCE_NAMESPACE, true);

  if (!full)
  {
    for (uint32_t i = 0; i < key_index_tmp; i++)
    {
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&data_tmp, sizeof(PersistenceData_t));
      bool finished = i + 1 == key_index_tmp;
      cb(key_buffer[i], &data_tmp, i + 1, finished);
    }
  }
  else
  {
    // 拼接
    uint32_t num = 0;
    for (uint32_t i = key_index_tmp; i < PERSISTENCE_KEY_MAX_NUM; i++)
    {
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&data_tmp, sizeof(PersistenceData_t));
      cb(key_buffer[i], &data_tmp, ++num, false);
    }
    for (uint32_t i = 0; i < key_index_tmp; i++)
    {
      pref_handler.getBytes(timestamp_key(key_buffer[i]), (uint8_t *)&data_tmp, sizeof(PersistenceData_t));
      bool finished = i + 1 == key_index_tmp;
      cb(key_buffer[i], &data_tmp, ++num, finished);
    }
  }

  pref_handler.end();
}

void Persistence_delete_all_data()
{
  pref_handler.begin(PERSISTENCE_NAMESPACE, false);
  for (uint32_t i = 0; i < PERSISTENCE_KEY_MAX_NUM; i++)
  {
    if (key_buffer[i] != 0)
    {
      LOG_INFO("delete key index: %d, %s...", i, timestamp_key(key_buffer[i]));
      pref_handler.remove(timestamp_key(key_buffer[i]));
    }
  }
  // 不能如下，否则meta也没了
  // bool res = pref_handler.clear();

  key_index = 0;
  memset(key_buffer, 0, sizeof(uint32_t) * PERSISTENCE_KEY_MAX_NUM);
  Persistence_write_meta();

  pref_handler.end();
}

// 可以通过遍历key_buffer非空即统计
Persistence_stats_t Persistence_get_stats()
{
  Persistence_stats_t stats = {};
  for (size_t i = 0; i < PERSISTENCE_KEY_MAX_NUM; i++)
  {
    if (key_buffer[i] != 0)
      stats.count++;
  }

  if (stats.count > 0)
  {
    if (key_buffer[(key_index + 1) % PERSISTENCE_KEY_MAX_NUM] != 0) // full
    {
      stats.full = true;
      stats.first_ts = key_buffer[key_index];
      stats.last_ts = key_buffer[key_index - 1];
    }
    else
    {
      stats.first_ts = key_buffer[0];
      stats.last_ts = key_buffer[key_index - 1];
    }
  }

  return stats;
}

void Persistence_write_data_test()
{
  LOG_INFO("free entries before write: %d", pref_handler.freeEntries());

  for (uint32_t i = 0; i < PERSISTENCE_KEY_MAX_NUM + 2; i++)
  {
    uint32_t ts = 842550703 + i;
    GS_result_t gs = {
        .tvoc = 1024 + i,
        .eco2 = 8 + i,
    };
    ES_result_t es = {
        .temperature = 34.56 + i,
        .humidity = 98.76 + i,
    };
    Persistence_write_data(ts, &gs, &es);
  }
  LOG_INFO("free entries after write: %d", pref_handler.freeEntries());

  // Serial.println("hello esp32");
  // Serial.println(ESP.getFreeHeap());
  // Serial.print("befor alloc:");
  // Serial.println(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

  // 需要启用psram才行: OPI PSRAM
  void *ptrVal = heap_caps_malloc(PERSISTENCE_KEY_MAX_NUM * sizeof(PersistenceData_t), MALLOC_CAP_SPIRAM);
  PersistenceData_t *tmp = (PersistenceData_t *)ptrVal;
  uint32_t s = Persistence_read_all_data(tmp);
  LOG_INFO("read all data: %d", s);

  for (uint32_t i = 0; i < s; i++)
  {
    LOG_DEBUG("[%d]: %s", i, Persistence_format(&(tmp[i])));
  }
  // Serial.print("after alloc:");
  // Serial.println(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

  Persistence_delete_all_data();

  // Serial.print("after alloc:");
  // Serial.println(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

  heap_caps_free(ptrVal);

  // Serial.print("after release:");
  // Serial.println(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

  LOG_INFO("free entries after delete: %d", pref_handler.freeEntries());
}

///////////////////////////////// v2 littlefs
Persistence P_LittleFS;

Persistence::Persistence()
{
}

Persistence::~Persistence()
{
  // 其实无用
  LittleFS.end();
}

bool Persistence::init()
{
  // note：后面所有操作都以FORMAT_LITTLEFS_IF_FAILED为base
  if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, PERSISTENCE_DATA_FILE_DIR))
  {
    LOG_ERROR("LittleFS Mount Failed");
    return false;
  }
  LOG_INFO("LittleFS initialized! total bytes: %d, used bytes: %d", LittleFS.totalBytes(), LittleFS.usedBytes());

  return true;
}

void Persistence::formatFS()
{
  LOG_INFO("start format LittleFS...");

  // 卸载文件系统（如果已挂载）
  if (LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, PERSISTENCE_DATA_FILE_DIR))
  {
    LittleFS.end();
    delay(100);
  }
  // 执行格式化
  bool formatted = LittleFS.format();

  if (formatted)
    LOG_INFO("LittleFS format success!");
  else
    LOG_ERROR("LittleFS format failed!");

  // 重新挂载文件系统
  this->init();
}

// 获取当前日期文件名
String Persistence::getCurrentFilename()
{
  Time t = get_time_from_ds1302();
  char buf[24];
  snprintf(buf, sizeof(buf), "/%04d%02d%02d.csv", t.yr, t.mon, t.date);
  return String(buf);
}

void Persistence::writeData(Timestamp_t ts, GS_result_t *gs, ES_result_t *es)
{
  String filename = this->getCurrentFilename();
  bool new_file = !LittleFS.exists(filename);

  // note：默认都是追加
  File file = LittleFS.open(filename, FILE_APPEND);

  if (!file)
  {
    LOG_ERROR("open file failed");
    return;
  }
  LOG_DEBUG("write data: %d, %d, %d, %.2f, %.2f, %.2f to file: %s",
            ts, gs->tvoc, gs->eco2, es->temperature, es->humidity, es->pressure/1000.0f, filename.c_str());

  if (new_file)
  {
    // CSV格式头：时间戳,TVOC,eCO2,温度,湿度,压力
    if (!file.printf("timestamp,tvoc(ppb),eco2(ppm),temperature(C),humidity(%%rH),pressure(kPa)\n"))
      LOG_ERROR("write header to file failed");
  }
  // CSV格式：时间戳,TVOC,eCO2,温度,湿度,压力
  // 20字节左右（因为是字符串）。30天大概900k
  if (!file.printf("%s,%d,%d,%.2f,%.2f,%.2f\n",
                   format_ds1302_timestamp_no_wday(ts),
                   gs->tvoc, gs->eco2,
                   es->temperature, es->humidity, es->pressure/float(1000)))
    LOG_ERROR("write data to file failed");

  file.close();

  this->cleanOldFiles();
}

Persistence_stats_t Persistence::getStats()
{
  Persistence_stats_t stats = {};

  File root = LittleFS.open("/");
  FileInfo first_file = {}, last_file = {};

  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory())
    {
      time_t lastWrite = file.getLastWrite();
      LOG_DEBUG("[ts] file %s last write time: %d", file.name(), lastWrite);

      if (first_file.lastWrite == 0 || lastWrite < first_file.lastWrite)
      {
        first_file.lastWrite = lastWrite;
        first_file.name = file.name();
        first_file.path = file.path();
        // first_file.f = file; note：这里最后会一直空
      }

      if (lastWrite > last_file.lastWrite)
      {
        last_file.lastWrite = lastWrite;
        last_file.name = file.name();
        last_file.path = file.path();
        // last_file.f = file;
      }

      // 打开文件并统计数据行数（排除表头）
      // todo 效率低下
      File dataFile = LittleFS.open(file.path());
      if (dataFile)
      {
        int lineCount = 0;
        // 读取所有行
        while (dataFile.available())
        {
          dataFile.readStringUntil('\n');
          lineCount++;
        }
        // 减去表头行（至少有一行表头）
        if (lineCount > 0)
          stats.count += (lineCount - 1);
        dataFile.close();
      }
      else
        LOG_WARN("Failed to open file for line counting: %s", file.name());
    }
    file.close();
    file = root.openNextFile();
  }
  root.close();

  // 找最新文件的最后一行数据，和最旧文件的第一行数据
  LOG_INFO("[ts] start find first file: %s, last write: %d \tlast file: %s, last write: %d",
           first_file.name, first_file.lastWrite, last_file.name, last_file.lastWrite);
  if (first_file.lastWrite && last_file.lastWrite)
  {
    // 读取最旧文件的第一行数据
    String oldestSecondLine = getSecondLine(first_file.path.c_str());

    // 读取最新文件的最后一行数据
    String latestLastLine = getLastLine(last_file.path.c_str());

    // 解析
    if (!oldestSecondLine.isEmpty())
    {
      stats.first_ts_raw = this->parseOneLineTsRaw(oldestSecondLine);
      stats.first_ts = stats.first_ts_raw.toInt();
    }
    if (!latestLastLine.isEmpty())
    {
      stats.last_ts_raw = this->parseOneLineTsRaw(latestLastLine);
      stats.last_ts = stats.last_ts_raw.toInt();
    }
  }

  return stats;
}

uint32_t Persistence::getFileCounts()
{
  uint32_t count = 0;
  File root = LittleFS.open("/");
  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory())
      count++;
    file.close();
    file = root.openNextFile();
  }
  root.close();
  return count;
}

void Persistence::cleanOldFiles()
{
  std::vector<FileInfo> files;

  // 收集所有文件信息
  File root = LittleFS.open("/");
  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory())
    {
      FileInfo info;
      info.name = file.name();
      info.path = file.path();
      info.lastWrite = file.getLastWrite();
      files.push_back(info);
      LOG_DEBUG("Found file: %s, last modified: %ld", info.name.c_str(), info.lastWrite);
    }
    file.close();
    file = root.openNextFile();
  }
  root.close();

  // 如果文件数不超过最大值，直接返回
  if (files.size() <= PERSISTENCE_MAX_FILES)
  {
    LOG_INFO("File count (%d) is within limit (%d), no files deleted", files.size(), PERSISTENCE_MAX_FILES);
    return;
  }

  // 按最后修改时间降序排序 (最新的在前)
  std::sort(files.begin(), files.end(), [](const FileInfo &a, const FileInfo &b)
            { return a.lastWrite > b.lastWrite; });

  // 删除多余的旧文件
  LOG_DEBUG("Need to delete %d old files (total: %d, keep: %d)",
            files.size() - PERSISTENCE_MAX_FILES, files.size(), PERSISTENCE_MAX_FILES);

  for (int i = PERSISTENCE_MAX_FILES; i < files.size(); i++)
  {
    String filePath = files[i].path;
    if (LittleFS.remove(filePath))
      LOG_INFO("Deleted old file: %s (last modified: %ld)", filePath.c_str(), files[i].lastWrite);
    else
      LOG_ERROR("Failed to delete file: %s", filePath.c_str());
  }
}

void Persistence::readAllData()
{
  File root = LittleFS.open("/");

  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory())
    {
      LOG_INFO("- read from file:%s", file.name());
      while (file.available())
      {
        Serial.write(file.read());
      }
    }
    file.close();
    file = root.openNextFile();
  }

  root.close();
}

void Persistence::deleteAllData()
{
  File root = LittleFS.open("/");
  std::vector<String> delete_files;

  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory())
    {
      LOG_DEBUG("- prepare to delete file:%s", file.name());
      delete_files.push_back(String("/") + String(file.name()));
      // note：不能直接在遍历中删除！否则Failed to unlink path "/20250713.csv". Has open FD
      // if (!LittleFS.remove(String("/") + String(file.name())))
      //   LOG_ERROR("Failed to delete file: %s", file.name());
    }
    file.close();
    file = root.openNextFile();
  }
  for (auto &file : delete_files)
  {
    if (!LittleFS.remove(file))
      LOG_ERROR("Failed to delete file: %s", file.c_str());
  }

  root.close();
}

void Persistence::Test_Stats()
{
  Persistence_stats_t stats = this->getStats();
  LOG_INFO("stats: \r\n  cnt:%d\r\n  first_ts:%ld\r\n  last_ts:%ld", stats.count, stats.first_ts, stats.last_ts);

  LOG_INFO("total bytes: %d(%dk), used bytes: %d(%dk)",
           LittleFS.totalBytes(), LittleFS.totalBytes() / 1024, LittleFS.usedBytes(), LittleFS.usedBytes() / 1024);
  this->listDir();
}

void Persistence::Test_Write()
{
  this->Test_Stats();
  for (uint32_t i = 0; i < 30; i++)
  {
    Time t(2025, 7, 1 + i, 14, 1 + i, 30, Time::kMonday);
    // sync_to_ds1302(&t);

    Timestamp_t ts = get_timestamp(&t);
    GS_result_t gs = {
        .tvoc = 1024 + i,
        .eco2 = 8 + i,
    };
    ES_result_t es = {
        .temperature = 34.56 + i,
        .humidity = 98.76 + i,
    };
    this->writeData(ts, &gs, &es);
    delay(500);
  }
  this->Test_Stats();

  this->readAllData();
  // this->deleteAllData();

  // this->Test_Stats();
}

void Persistence::listDir(const char *dirname, uint8_t levels)
{
  LOG_INFO("Listing directory: %s", dirname);

  File root = LittleFS.open(dirname);
  if (!root)
  {
    LOG_ERROR("- failed to open directory");
    return;
  }
  if (!root.isDirectory())
  {
    LOG_ERROR(" - not a directory");
    root.close();
    return;
  }

  File file = root.openNextFile();
  while (file)
  {
    if (file.isDirectory())
    {
      LOG_INFO("  DIR : %s", file.name());
      if (levels)
      {
        this->listDir(file.path(), levels - 1);
      }
    }
    else
    {
      LOG_INFO("  FILE: %s\tSIZE: %d(%dk)", file.name(), file.size(), file.size() / 1024);
    }
    file.close();
    file = root.openNextFile();
  }
  root.close();
}

Timestamp_t Persistence::parseOneLineTs(String line)
{
  Timestamp_t ts = this->parseOneLineTsRaw(line).toInt();
  return ts;
}

String Persistence::parseOneLineTsRaw(String line)
{
  String tsStr = "";
  int commaIndex = line.indexOf(',');
  if (commaIndex > 0)
  {
    tsStr = line.substring(0, commaIndex);
  }
  else
  {
    LOG_WARN("Invalid format for line: %s", line.c_str());
  }
  return tsStr;
}

String Persistence::getSecondLine(const char *path)
{
  String line = "";
  File f = LittleFS.open(path);
  if (f)
  {
    // 跳过第一行
    f.readStringUntil('\n');
    // 读取第二行数据
    line = f.readStringUntil('\n');
    LOG_DEBUG("Second line: %s", line.c_str());
    f.close();
  }
  else
    LOG_ERROR("Failed to open file: %s", path);
  return line;
}

String Persistence::getLastLine(const char *path)
{
  String line = "";
  File f = LittleFS.open(path);
  if (f)
  {
    // 定位到文件末尾
    int fileSize = f.size();
    int pos = fileSize - 1;

    // 跳过末尾的换行符
    while (pos >= 0)
    {
      f.seek(pos);
      char c = f.read();
      if (c != '\n')
        break;
      pos--;
    }

    // 从末尾向前查找换行符
    while (pos > 0)
    {
      f.seek(pos);
      char c = f.read();
      if (c == '\n')
      {
        // 找到换行符，读取下一行
        line = f.readStringUntil('\n');
        break;
      }
      pos--;
    }

    // 如果没有找到换行符，读取整个文件内容
    if (line.isEmpty() && fileSize > 0)
    {
      LOG_WARN("Failed to find newline in file, try to read all");
      f.seek(0);
      line = f.readStringUntil('\n');
    }

    LOG_DEBUG("Last line: %s", line.c_str());
    f.close();
  }
  else
    LOG_ERROR("Failed to file: %s", path);

  return line;
}
